package com.fypg.module.blooddonation.api.service.bloodDonation;

import com.fypg.framework.common.exception.ErrorCode;
import com.fypg.framework.common.exception.util.ServiceExceptionUtil;
import com.fypg.framework.common.util.date.DateUtils;
import com.fypg.module.blooddonation.api.client.dal.bloodPlatform.*;
import com.fypg.module.blooddonation.api.client.dal.bloodStation.*;
import com.fypg.module.blooddonation.api.client.enums.*;
import com.fypg.module.blooddonation.api.client.service.bloodDonationAPI.BloodDonationAPIService;
import com.fypg.module.blooddonation.api.client.utils.DataDictionaryHelper;
import com.fypg.module.blooddonation.api.controller.app.bloodDonation.vo.*;
import com.fypg.module.blooddonation.api.dto.bloodDonationSchedule.BloodDonationScheduleList;
import com.fypg.module.blooddonation.api.dto.wanda.*;
import com.fypg.module.blooddonation.api.service.wanDaAPIService.WanDaAPIService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import static com.fypg.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * 献血服务实现类
 * @author 盘古
 */
@Service
@RequiredArgsConstructor
public class BloodDonationServiceImpl implements BloodDonationService {

    private final BloodDonationAPIService apiService;
    private final WanDaAPIService wanDaAPIService;

    // 最小预约时间（小时）
    @Value("${BookingTime.BookingMinimumTime:24}")
    private double bookingMinimumTime;

    // 最大预约时间（天）
    @Value("${BookingTime.BookingMaximumTime:7}")
    private double bookingMaximumTime;

    /**
     * 获取解密信息_万达健康身份信息
     */
    @Override
    public GetDecryptInfoOutput getDecryptInfo(GetDecryptInfoInput inputModel) {
        // 校验渠道类型
        if (!StringUtils.hasText(inputModel.getChannelType())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PARAM_ERROR, "渠道类型不能为空");
        }

        if (!"1".equals(inputModel.getChannelType())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PARAM_ERROR,
                    "不支持的渠道类型：" + inputModel.getChannelType());
        }

        // 构建请求DTO
        ReqGetDencryptDataDTO requestDTO = new ReqGetDencryptDataDTO();
        requestDTO.setBusinessData(new ReqGetDencryptDataModel(
                inputModel.getOpenID(),
                inputModel.getEncryptData()
        ));

        // 调用万达API获取解密数据
        ApiResultDTO apiResultDTO = wanDaAPIService.getDencryptData(requestDTO);
        if (apiResultDTO == null) {
            String errorMsg = apiResultDTO != null ? apiResultDTO.getMessage() : "接口调用失败";
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.BLOOD_DATA_DECRYPT_FAILED, errorMsg);
        }

        // 构建返回对象
        GetDecryptInfoOutput result = new GetDecryptInfoOutput();
        GetDecryptInfoOutput.GetDecryptInfoModel data = new GetDecryptInfoOutput.GetDecryptInfoModel();
        data.setCardType(apiResultDTO.getBusinessData().getZJLX());
        data.setCardNO(apiResultDTO.getBusinessData().getZJHM());
        data.setPatientName(apiResultDTO.getBusinessData().getXM());
        result.setData(data);

        return result;
    }

    /**
     * 获取字典类型列表
     */
    @Override
    public List<DictionaryItem> getDictionaryTypeList(DictionaryReqVO inputModel) {
        // 是否清除缓存
        if (inputModel.getIsRemoveCache() != null && inputModel.getIsRemoveCache()) {
            apiService.removeDictionaryCache(CacheConstants.BLOOD_DICT + inputModel.getTypeCode());
        }

        // 构建请求对象并获取字典列表
        DictionaryReq dictionaryReq = new DictionaryReq();
        dictionaryReq.setTypeCode(inputModel.getTypeCode());
        dictionaryReq.setBloodStationEncode(inputModel.getBloodStationEncode());
        return apiService.getDic(dictionaryReq);

    }

    /**
     * 获取献血排班列表
     */
    @Override
    public List<GetBloodDonationScheduleListRespVO> getBloodDonationScheduleList(GetBloodDonationScheduleListReqVO inputModel) {
        // 获取预约时间段人数
        TimeAppCountReq request = new TimeAppCountReq();
        request.setAppDate(inputModel.getAppDate());
        request.setBloodStationEncode(inputModel.getBloodStationEncode());
        List<TimeAppCountData> list = apiService.getTimeAppCount(request);
        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }

        // 判断献血类型名称
        String donName =
                BloodDonationType.WHOLE_BLOOD.getCode().equals(inputModel.getBloodDonationType())
                        ? BloodDonationType.WHOLE_BLOOD.getDesc() : BloodDonationType.APHERESIS.getDesc();

        // 按场所分组并构建返回对象
        return list.stream()
                .collect(Collectors.groupingBy(TimeAppCountData::getPlaceName))
                .entrySet().stream()
                .map(entry -> {
                    String placeName = entry.getKey();
                    TimeAppCountData first = entry.getValue().get(0);

                    List<BloodDonationScheduleList> scheduleList = entry.getValue().stream()
                            .filter(p -> donName.equals(p.getDonType()))
                            .collect(Collectors.groupingBy(TimeAppCountData::getBeginTime))
                            .values().stream()
                            .map(group -> {
                                TimeAppCountData item = group.get(0);
                                return new BloodDonationScheduleList(
                                        item.getBeginTime(),
                                        item.getEndTime(),
                                        String.valueOf(item.getSetCount()),
                                        String.valueOf(item.getCanAppCount())
                                );
                            }).collect(Collectors.toList());

                    GetBloodDonationScheduleListRespVO vo = new GetBloodDonationScheduleListRespVO();
                    vo.setBloodDonationDate(inputModel.getAppDate());
                    vo.setBloodDonationEncode(first.getPlaceId());
                    vo.setBloodDonationName(placeName);
                    vo.setBloodDonationScheduleList(scheduleList);

                    return vo;
                }).collect(Collectors.toList());
    }

    /**
     * 获取献血详情
     */
    @Override
    public GetBloodDonationDetailRespVO getBloodDonationDetail(GetBloodDonationDetailReqVO inputModel) {
        // 构建请求对象获取献血预约信息
        GetBloodAppReq bloodAppRequest = new GetBloodAppReq();
        bloodAppRequest.setName(inputModel.getMedicalCardInfo().getPatientName());
        bloodAppRequest.setIdType(inputModel.getMedicalCardInfo().getCardType());
        bloodAppRequest.setIdNum(inputModel.getMedicalCardInfo().getCardNO());
        bloodAppRequest.setBloodStationEncode(inputModel.getBloodStationEncode());

        AppointmentResultData bloodAppData = apiService.getBloodApp(bloodAppRequest);
        if(bloodAppData == null) {
            return null;
        }

        LocalDateTime appDate = LocalDateTime.parse(
                bloodAppData.getAppDate(),
                DateTimeFormatter.ofPattern(DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
        );

        // 构建返回对象并设置基础信息
        GetBloodDonationDetailRespVO resp = new GetBloodDonationDetailRespVO();
        resp.setName(bloodAppData.getName());
        resp.setCardType(bloodAppData.getIdType());
        resp.setCardNO(bloodAppData.getIdNum());
        resp.setNation(getDictionaryTuple(DictConstants.T_DEF_NATION, bloodAppData.getNationId(),inputModel.getBloodStationEncode()));
        resp.setHouseholdRegister(getDictionaryTuple(DictConstants.T_DEF_ORIGIN, bloodAppData.getOriginId(),inputModel.getBloodStationEncode()));
        resp.setMaritalStatus(getDictionaryTuple(DictConstants.T_DEF_MARITAL, bloodAppData.getMaritalId(),inputModel.getBloodStationEncode()));
        resp.setEducationalLevel(getDictionaryTuple(DictConstants.T_DEF_EDUCATION, bloodAppData.getEducationId(),inputModel.getBloodStationEncode()));
        resp.setPhone(bloodAppData.getPhone());
        resp.setWorkType(getDictionaryTuple(DictConstants.T_DNM_DEF_OCCUPATION, bloodAppData.getOccupationId(),inputModel.getBloodStationEncode()));
        resp.setBloodDonationEncode(bloodAppData.getPlaceId());
        resp.setBloodDonationPlace(bloodAppData.getPlaceName());
        resp.setBloodDonationType("101".equals(bloodAppData.getDonKindId()) ? BloodDonationType.WHOLE_BLOOD.getCode() : BloodDonationType.APHERESIS.getCode());
        resp.setBloodDonationDate(appDate.format(DateTimeFormatter.ofPattern(DateUtils.FORMAT_YEAR_MONTH_DAY)));
        resp.setBloodDonationTime(appDate.format(DateTimeFormatter.ofPattern("HH:mm")));
        resp.setBloodDonationStatus(DataDictionaryHelper.getBloodDonationStatus(bloodAppData.getStatus()));

        // 调用献血核查接口
        DonorChkReq donorChkRequest = new DonorChkReq();
        donorChkRequest.setIdType(inputModel.getMedicalCardInfo().getCardType());
        donorChkRequest.setIdNum(inputModel.getMedicalCardInfo().getCardNO());
        donorChkRequest.setOrgId(inputModel.getBloodStationEncode());
        donorChkRequest.setChkDate(resp.getBloodDonationDate());
        donorChkRequest.setDonType(resp.getBloodDonationType());

        DonorChkData donorData = apiService.donorCheck(donorChkRequest);

        if (donorData != null) {
            String discardReason = DataDictionaryHelper.getDictionaryTypeValue(
                    "00000", "8", donorData.getDonStatus());

            if (StringUtils.hasText(discardReason)) {
                resp.setBloodDonationCount(donorData.getDonCount());
                resp.setBloodDonationTotalVolume(donorData.getDonVol());
                resp.setAllowBloodDonationDate(donorData.getAllowDonDate());
                resp.setDiscardReason(discardReason);
                resp.setBookingSuccess(false);
                return resp;
            } else {
                resp.setBookingSuccess(true);
            }
        } else {
            resp.setBookingSuccess(true);
        }

        return resp;
    }

    /**
     * 获取献血记录信息
     */
    @Override
    public GetBloodDonationRecordInfoRespVO getBloodDonationRecordInfo(GetBloodDonationRecordInfoReqVO inputModel) {
        // 构建查询请求
        DonRecordQueryReq testRequest = new DonRecordQueryReq();
        testRequest.setIdType(inputModel.getMedicalCardInfo().getCardType());
        testRequest.setIdNum(inputModel.getMedicalCardInfo().getCardNO());

        DonchkTestResultsResp getBloodDonationInfo = apiService.donchkTestResults(testRequest);

        if (getBloodDonationInfo == null) {
            throw exception(ErrorCodeConstants.BLOOD_RECORD_QUERY_FAILED);
        }

        // 构建返回对象
        GetBloodDonationRecordInfoRespVO result = new GetBloodDonationRecordInfoRespVO();
        result.setABOBloodType(getBloodDonationInfo.getAbo());
        result.setRHBloodType(getBloodDonationInfo.getRh());
        result.setBloodDonationCount(getBloodDonationInfo.getDonCount());
        result.setBloodDonationTotalVolume(getBloodDonationInfo.getVolumeTotal());

        // 构建详细记录列表
        List<GetUserBloodDonationDetail> details = getBloodDonationInfo.getData().stream()
                .map(item -> new GetUserBloodDonationDetail(
                        item.getCollectDate(),
                        item.getDonKind(),
                        String.valueOf(item.getVolume()),
                        DataDictionaryHelper.getDictionaryTypeValue("00000", "7", item.getOrgId()),
                        item.getTotalTestResult()
                ))
                .sorted(Comparator.comparing(GetUserBloodDonationDetail::getBloodDonationDate).reversed())
                .collect(Collectors.toList());

        result.setGetUserBloodDonationDetails(details);

        return result;
    }

    /**
     * 新增献血预约
     */
    @Override
    public AddBloodDonationOrderRespVO addBloodDonationOrder(AddBloodDonationOrderReqVO inputModel) {
        String dateTimeStr = inputModel.getBloodDonationDate() + " " + inputModel.getBloodDonationEndTime();
        LocalDateTime bloodDonationDate = LocalDateTime.parse(dateTimeStr,
                DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));

        // 参数业务逻辑校验
        if (bloodDonationDate.isBefore(LocalDateTime.now().plusHours((long) bookingMinimumTime))) {
            throw exception(new ErrorCode(ErrorCodeConstants.BLOOD_APPOINTMENT_TIME_INVALID.getCode(),
                            "请提前" + bookingMinimumTime + "小时预约"    )
                    );
        }

        if (bloodDonationDate.isAfter(LocalDateTime.now().plusDays((long) bookingMaximumTime))) {
            throw exception(new ErrorCode(ErrorCodeConstants.BLOOD_APPOINTMENT_TIME_INVALID.getCode(),
                            "预约时间超过" + bookingMaximumTime + "天，请重新选择")
                    );
        }

        // 查询是否已献血成功或符合预约条件
        DonRecordQueryReq testRequest = new DonRecordQueryReq();
        testRequest.setIdType(inputModel.getMedicalCardInfo().getCardType());
        testRequest.setIdNum(inputModel.getMedicalCardInfo().getCardNO());

        DonchkTestResultsResp donchkTestResults = apiService.donchkTestResults(testRequest);
        if (donchkTestResults != null && !donchkTestResults.getData().isEmpty()) {
            boolean isExitInfo = donchkTestResults.getData().stream()
                    .anyMatch(p -> p.getCollectDate().startsWith(bloodDonationDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));

            if (isExitInfo) {
                throw exception(ErrorCodeConstants.BLOOD_APPOINTMENT_DUPLICATE);
            }

            DonorChkReq donorChkRequest = new DonorChkReq();
            donorChkRequest.setIdType(inputModel.getMedicalCardInfo().getCardType());
            donorChkRequest.setIdNum(inputModel.getMedicalCardInfo().getCardNO());
            donorChkRequest.setOrgId(inputModel.getBloodDonationEncode());
            donorChkRequest.setChkDate(bloodDonationDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            donorChkRequest.setDonType(inputModel.getBloodDonationType());

            DonorChkData donorData = apiService.donorCheck(donorChkRequest);
            if (donorData != null) {
                LocalDateTime allowBloodDonationDate = LocalDateTime.parse(donorData.getAllowDonDate(),
                        DateTimeFormatter.ofPattern(DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND));

                if (bloodDonationDate.isBefore(allowBloodDonationDate)) {
                    throw exception(ErrorCodeConstants.BLOOD_APPOINTMENT_FAILED,
                            "您已献血成功，请于" + allowBloodDonationDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "后再预约");
                }

                String discardReason = DataDictionaryHelper.getDictionaryTypeValue(
                        "00000", "8", donorData.getDonStatus());

                if (StringUtils.hasText(discardReason)) {
                    throw exception(ErrorCodeConstants.BLOOD_DONOR_CHECK_FAILED, discardReason);
                }
            }
        }

        // 查询是否已预约成功
        GetBloodAppReq bloodAppRequest = new GetBloodAppReq();
        bloodAppRequest.setName(inputModel.getMedicalCardInfo().getPatientName());
        bloodAppRequest.setIdType(inputModel.getMedicalCardInfo().getCardType());
        bloodAppRequest.setIdNum(inputModel.getMedicalCardInfo().getCardNO());
        bloodAppRequest.setBloodStationEncode(inputModel.getBloodStationEncode());

        AppointmentResultData bloodAppData = apiService.getBloodApp(bloodAppRequest);
        if (bloodAppData != null) {
            LocalDateTime existingAppDate = LocalDateTime.parse(bloodAppData.getAppDate(),
                    DateTimeFormatter.ofPattern(DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND));

            boolean isExitBookingInfo = existingAppDate.toLocalDate().equals(bloodDonationDate.toLocalDate());
            if (isExitBookingInfo) {
                throw exception(ErrorCodeConstants.BLOOD_APPOINTMENT_DUPLICATE, "当前日期已预约成功");
            }
        }

        // 构造预约ID
        String appointmentId = inputModel.getBloodStationEncode() +
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddhhmmss"));

        // 构造献血预约请求对象
        BloodAppReq bloodApp = new BloodAppReq();
        bloodApp.setAppId(appointmentId);
        bloodApp.setName(inputModel.getMedicalCardInfo().getPatientName());
        bloodApp.setIdType(inputModel.getMedicalCardInfo().getCardType());
        bloodApp.setIdNum(inputModel.getMedicalCardInfo().getCardNO());
        bloodApp.setAppDate(inputModel.getBloodDonationDate() + " " + inputModel.getBloodDonationBeginTime());
        bloodApp.setAppEndDate(inputModel.getBloodDonationDate() + " " + inputModel.getBloodDonationEndTime());
        bloodApp.setDonKindId(Integer.valueOf(inputModel.getBloodDonationType()));
        bloodApp.setPlaceId(inputModel.getBloodDonationEncode());
        bloodApp.setPhone(inputModel.getPhone());
        bloodApp.setAddress(inputModel.getAddress());
        bloodApp.setNationId(inputModel.getNationType());
        bloodApp.setOriginId(inputModel.getHouseholdRegisterType());
        bloodApp.setOccupationId(inputModel.getWorkType());
        bloodApp.setMaritalId(inputModel.getMaritalStatusType());
        bloodApp.setEducationId(inputModel.getEducationalLevelType());
        bloodApp.setBloodStationEncode(inputModel.getBloodStationEncode());

        apiService.bloodApp(bloodApp);

        // 构建返回对象
        AddBloodDonationOrderRespVO result = new AddBloodDonationOrderRespVO();
        result.setSuccess(true);
        return result;
    }

    /**
     * 获取字典编码和名称
     */
    private String getDictionaryTuple(String dictionaryType, String dictionaryEncode,String bloodStationEncode) {
        if (!StringUtils.hasText(dictionaryType) || !StringUtils.hasText(dictionaryEncode)) {
            return "";
        }

        DictionaryReq request = new DictionaryReq();
        request.setTypeCode(dictionaryType);
        request.setBloodStationEncode(bloodStationEncode);
        List<DictionaryItem> getDictionaryTypeList = apiService.getDic(request);

        return getDictionaryTypeList.stream()
                .filter(p -> dictionaryEncode.equals(p.getId()))
                .findFirst()
                .map(DictionaryItem::getName)
                .orElse("");
    }
}
